#region Header Licence
//  NKnobMan, Copyright 2009, Alexandre MUTEL.
// 
//  This is free software; you can redistribute it and/or modify it
//  under the terms of the GNU Lesser General Public License as
//  published by the Free Software Foundation; either version 2.1 of
//  the License, or (at your option) any later version.
// 
//  This software is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
//  Lesser General Public License for more details.
// 
//  You should have received a copy of the GNU Lesser General Public
//  License along with this software; if not, write to the Free
//  Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
//  02110-1301 USA, or see the FSF site: http://www.fsf.org.
#endregion

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Windows;
using System.Windows.Data;

namespace NKnobMan
{
    [DataContract]
    public class Layer
    {
        private KnobMan _knobMan;
        private int _id;

        public Layer(KnobMan knobMan, int layerId)
        {
            _knobMan = knobMan;
            _id = layerId;
            Primitive = new LayerPrimitive(this);
            Layout = new LayerLayout(this);
            Colors = new LayerColors(this);
            Mask = new LayerMask(this);
            Lighting = new LayerLighting(this);
        }

        [DataMember(Order=0)]
        public int Id
        {
            get { return _id; }
            private set { _id = value; }
        }

        [DataMember(Order=1)]
        public string Name
        {
            get { return GetValue<string>("Name"); }
            set { SetValue("Name", value); }
        }

        [DataMember(Order = 2)]
        public LayerPrimitive Primitive { get; private set; }

        [DataMember(Order = 3)]
        public LayerLayout Layout { get; private set; }

        [DataMember(Order = 4)]
        public LayerColors Colors { get; private set; }

        [DataMember(Order = 5)]
        public LayerMask Mask { get; private set; }

        [DataMember(Order = 6)]
        public LayerLighting Lighting { get; private set; }


        private static string[] staticCommands = null;

        public static string[] GetKnobManCommands()
        {
            if (staticCommands == null)
            {
                PropertyInfo[] propertyInfos = typeof (Layer).GetProperties();
                List<PropertyInfo> stack = new List<PropertyInfo>();
                List<string> commands = new List<string>();
                foreach (PropertyInfo property in propertyInfos)
                {
                    RecurseOnType(property, stack, commands);
                }
                staticCommands = commands.ToArray();
            }
            return staticCommands;
        }

        public void SetValueFromPath(string path, string value)
        {
            object obj;
            PropertyInfo info;
            GetPropertyInfoFromPath(path, out info, out obj);
            TypeConverter conv = TypeDescriptor.GetConverter(info.PropertyType);
            object typedValue = conv.ConvertFrom(value);
            info.SetValue(obj, typedValue, null);            
        }

        public string GetValueFromPath(string path)
        {
            object obj;
            PropertyInfo info;
            GetPropertyInfoFromPath(path, out info, out obj);
            TypeConverter conv = TypeDescriptor.GetConverter(info.PropertyType);
            return conv.ConvertTo(info.GetValue(obj, null), typeof (string)) as string;
        }
 
        private void GetPropertyInfoFromPath(string pathArg, out PropertyInfo info, out object obj )
        {
            string[] pathList = pathArg.Split('.');
            Type type = typeof(Layer);
            obj = this;
            info = null;
            for (int i = 0; i < pathList.Length; i++)
            {
                string path = pathList[i];
                info = type.GetProperty(path);
                type = info.PropertyType;
                if (i < (pathList.Length - 1))
                {
                    obj = info.GetValue(obj, null);
                }
            }
            //TypeConverter conv = TypeDescriptor.GetConverter(type);
            //object typedValue = conv.ConvertFrom(value);
            //info.SetValue(obj, typedValue, null);            
        }

        //public void ApplyCommand(string command, string value)
        //{
        //    string[] pathList = command.Split('.');
        //    Type type = typeof (Layer);
        //    PropertyInfo info = null;
        //    object obj = this;
        //    for(int i = 0; i < pathList.Length; i++)
        //    {
        //        string path = pathList[i];
        //        info = type.GetProperty(path);
        //        type = info.PropertyType;
        //        if (i < (pathList.Length - 1))
        //        {
        //            obj = info.GetValue(obj, null);
        //        }
        //    }

        //}


        private static void RecurseOnType(PropertyInfo property, List<PropertyInfo> stack, List<string> commands)
        {
            KnobManFieldAttribute knobManFieldAttribute =
                Attribute.GetCustomAttribute(property, typeof(KnobManFieldAttribute)) as KnobManFieldAttribute;

            if ( knobManFieldAttribute != null && ( 
                ! property.PropertyType.IsAssignableFrom(typeof(IntAnimateLayerItem)) 
                && ! property.PropertyType.IsAssignableFrom(typeof(FloatAnimateLayerItem)) ))
            {
                StringBuilder commandBuilder = new StringBuilder();
                foreach (PropertyInfo infoInStack in stack)
                {
                    commandBuilder.Append(infoInStack.Name);
                    commandBuilder.Append('.');
                }
                commandBuilder.Append(property.Name);
                commands.Add(commandBuilder.ToString());
            }
            else if (!property.PropertyType.IsPrimitive && Attribute.GetCustomAttribute(property, typeof(DataMemberAttribute)) != null)
            {
                if ( property.PropertyType.IsAssignableFrom(typeof(IntAnimateLayerItem)) || 
                    property.PropertyType.IsAssignableFrom(typeof(FloatAnimateLayerItem)) )
                {
                    Console.Out.WriteLine();
                }
                foreach (PropertyInfo childProperty in property.PropertyType.GetProperties())
                {
                    stack.Add(property);
                    RecurseOnType(childProperty, stack, commands);
                    stack.Remove(property);
                }
            }
        }

        


        public void Select()
        {
            _knobMan.NativeSetText("KnobManCtl:SetCurrentLayer " + Id);
        }

        internal T GetValue<T>(string propertyName, string knobName)
        {
            return _knobMan.GetValue<T>(Id, propertyName, knobName);
        }

        internal T GetValue<T>(string knobName)
        {
            return _knobMan.GetValue<T>(Id, knobName);
        }

        internal void SetValue<T>(string propertyName, string knobName, T value)
        {
            _knobMan.SetValue(Id, propertyName, knobName, value);
        }

        internal void SetValue<T>(string knobName, T value)
        {
            _knobMan.SetValue(Id, knobName, value);
        }
    }
}